import abc
import time
import datetime
from wx.lib.pubsub import setuparg1
from wx.lib.pubsub import pub

import DataManager

class ModelBaseList():
    """
    base class to implement view with list
    """
    def __init__(self):
        self.data = []
        self.load()

    def direct_append(self, listitem, send_message=1):
        self.data.append(listitem)
        #do sql
        DataManager.DataManager.db.add(self.TABLE_NAME, listitem.to_sql_values())
        #notify view that it should change to reflect the change of model
        if send_message == 1:
            pub.sendMessage(self.M_ADD_ITEM, data=listitem)
        
    def append(self, alist):
        assert(type(alist) == list)
        assert(type(alist[1]) == datetime.datetime)
        alist = ListItem(alist)
        self.direct_append(alist)

    def build_append(self, l):
        #this is a more convinient interface

        #generate ctime and mtime
        now = int(time.time())
        #status
        l.append((1<<9)-1)#mark all slots as modified
        l.append(now)
        l.append(now)
        self.append(l)

    def delete(self, item):
        #NOTE: remove item(and send out message, BEFORE you change it's mtime,
        #because otherwise, it will not be identified!)
        self.data.remove(item)
        DataManager.DataManager.db.mark_delete(self.TABLE_NAME,
                                               item.sql_condition(),
                                               int(time.time()))
        pub.sendMessage(self.M_DEL_ITEM, data=item)

    def modify(self, l, new_contents):
        #find out the original one from database
        #compare all the information fields of original item and
        #l(that doesn't include ctime and mtime). record modified fields in stat

        #the first part is quite similar to delete, except that it a complete
        #delete, not any trace left
        dfts = datetime.datetime.fromtimestamp
        DataManager.DataManager.db.delete(self.TABLE_NAME, l.sql_condition())
        self.data.remove(l)
        pub.sendMessage(self.M_DEL_ITEM, data=l)

        state = 0
        for j,i in enumerate(new_contents):
            if i != l.data[j]:
                l.data[j] = i
                state |= 1<<j
        if state == 0:
            return
        #NOTE: you couldn't modify anything that is already deleted, so there's no -1 case
        assert(l.stat >= 0)
        l.stat += state
        l.mt = int(time.mktime(datetime.datetime.now().timetuple()))
        self.direct_append(l)
        

        

    def merge(self, d):
        #1. find out if there is an item with the same ctime,
        #if there is no one, simply add
        #2. if there is one, check mtime
        #3. if we have a newer one, do nothing
        #4. if we got a new one, use the dict to construct a ListItem and
        #replace the original one
        dfts = datetime.datetime.fromtimestamp
        #NOTE: we do this because we also need to deal with the case in which this item
        #is already deleted
        #NOTE: for the sake of efficiency, we do not attempt to keep ui and data sycnchronized during merge
        l = DataManager.DataManager.db.querry('select * from %s where ct == %d'%(self.TABLE_NAME, d['ctime']))
        if len(l) == 0:
            #this is a add case
            #NOTE: the later 2 fields need no convertion
            if d['status'] == 1:
                #this is a delete case, and we do not have this shit, so skip it
                return
            
            item = ListItem([d['title'], dfts(d['start']), dfts(d['end']), d['place'],
                     d['desc'], d['kind'], d['repetition'], d['reminder'],
                     d['priority'], d['status'], d['ctime'], d['mtime']])
            self.direct_append(item, send_message=0)
        else:
            #check mtime
            if d['mtime'] < l[0][11]:
                #we got a old one, so we ignore it
                pass
            else:
                ctime = d['ctime']
                #get rid of what we have, replace it with the new one
                DataManager.DataManager.db.delete(self.TABLE_NAME, 'ct == %d'%(ctime))
                #ok, this will be enough for delete case
                if d['status'] == 1:
                    return
                #we never need to convert ctime and mtime to datetime
                item = ListItem([d['title'], dfts(d['start']), dfts(d['end']), d['place'],
                                d['desc'],  d['kind'], d['repetition'], d['reminder'],
                                 d['priority'], d['status'], d['ctime'], d['mtime']])
                self.direct_append(item, send_message=0)



    def load(self):
        l = DataManager.DataManager.db.querry("select * from %s where stat != -1"%(self.TABLE_NAME))
        for i in l:
            #print(l)
            tmp = list(i)
            tmp[1] = datetime.datetime.fromtimestamp(tmp[1])
            tmp[2] = datetime.datetime.fromtimestamp(tmp[2])
            self.data.append(ListItem(tmp))

    def refresh(self):
        #delete all and load all
        del self.data[:]
        self.load()
        pub.sendMessage(self.M_REFRESH_ITEM, None)

    def get(self):
        return self.data

    @abc.abstractproperty
    def TABLE_NAME(self):
        """
        the name of the table to write to
        """
        pass

class ListItem(object):
    """
    name changes nothing, they are the same
    """

    #stat = -1 means marked as deleted
    __slots__ = ['title', 'start', 'end', 'where',
                 'desc', 'type', 'repe', 'rmnd',
                 'prio', 'stat', 'ct', 'mt', 'data']

    names = ('title', 'start', 'end', 'place', 'desc', 'kind', 'repetition',
             'reminder', 'priority', 'status', 'ctime', 'mtime')
    def __init__(self, alist):
        #do not count data in, for it's a internal data
        assert(len(alist) == (len(self.__slots__)-1))
        object.__setattr__(self, 'data', list(alist))

    def __setattr__(self, name, val):
        idx = self.__slots__.index(name)
        self.data[idx] = val

    def __getattr__(self, name):
        idx = self.__slots__.index(name)
        return self.data[idx]

    def to_list(self):
        return self.data

    def to_sql_values(self):
        start = int(time.mktime(self.data[1].timetuple()))
        end = int(time.mktime(self.data[2].timetuple()))
        d = self.data
        #print("\n\n\n")
        #print(d)
        #print("\n\n\n")
        return "('%s',%d,%d,'%s','%s','%s',%d,%d,%d,%d,%d,%d)"%(
            d[0],start,end,d[3],d[4],d[5],
            d[6],d[7],d[8],d[9],d[10],d[11])

    @classmethod
    def get_diff(cls, l):
        state = l[9]
        d = {}
        d['ctime'] = l[10]
        d['mtime'] = l[11]
        d['stime'] = int(time.time())
        d['status'] = 0
        #TODO:make it faster, clearer
        for i in range(0, 9):
            j = (1<<i)
            if j > state:
                break
            if state & j:
                d[cls.names[i]] = l[i]
            else:
                continue
        return d
                

    @staticmethod
    def deal_escape(st):
        last = 0
        current = 0
        tmp = []
        ret
        for j,i in enumerate(st):
            if i == "'":
                tmp.append(st[last:current])
                last = current
        tmp.append(st[last:current])
        return ''.join(tmp)
            

    def sql_condition(self):
        #ct in int form
        return "ct = %d"%(self.data[10])

    @staticmethod
    def get_template():
        #may be useful
        return ['', datetime.datetime.now(),
                datetime.datetime.now(), '',
                '', ' _ ', 0, 0, 1]
